package com.witframework.core.persistence;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.SimpleExpression;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.ObjectRetrievalFailureException;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;

import com.witframework.WitException;
import com.witframework.core.ApplicationProperties;
import com.witframework.core.BaseEntity;
import com.witframework.core.BeanFactory;
import com.witframework.util.BeanUtils;
import com.witframework.util.DateUtil;
import com.witframework.util.TimestampConverter;

/**
 * dao的hibernate实现的父类，提供了基本的CRUD以及分页查询方法。 可以通过DAOFactory.getDAO()获取DAO实现的实例.
 *
 * @see BeanFactory#getBean(String)
 *      更多的方法可以通过HibernateDaoSupport父类的HibernateTemplate方法获得。
 * @see HibernateDaoSupport
 * @author westerasoft
 *
 */
public  abstract class BaseHibernateDao<T> extends HibernateDaoSupport implements IBaseDao<T>{
	
	protected Class<T> entityClass=null;// DAO所管理的Entity类型.
	
	public BaseHibernateDao() {	
		String cacheClasses=ApplicationProperties.getProperty("hibernate.cache.class","");		
		try{
			this.entityClass = (Class<T>) ((ParameterizedType) getClass()
                .getGenericSuperclass()).getActualTypeArguments()[0];
		}catch(Exception d){}
		
	}


	@Autowired
	protected void setSessionFactory0(SessionFactory sessionFactory) {
		super.setHibernateTemplate(super.createHibernateTemplate(sessionFactory));
	}

	public Class<T> getEntityClass() {		
		return entityClass;
	}
	
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#findById(java.io.Serializable)
	 */
	public T findById(Serializable id) throws DataAccessException {		
		if(id!=null)
			return (T) getHibernateTemplate().get(getEntityClass(), id);
		return null;
	}
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#findById(java.io.Serializable, boolean)
	 */
	public T findById(Serializable id, boolean lock) {
        T entity;
        if (lock)
            entity = (T) getHibernateTemplate().load(getEntityClass(), id,
                    LockMode.UPGRADE);
        else
            entity = findById(id);
        return entity;
    }
	public int getAllSize() throws DataAccessException{	
		int totalCount =((Integer) this.createCriteria().setProjection(Projections.rowCount()).uniqueResult()).intValue();
		return totalCount;
	}
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#getAll()
	 */
	public List<T> getAll() throws DataAccessException {
		List l=getHibernateTemplate().loadAll(getEntityClass());
		//getHibernateTemplate().initialize(l);
		return l;
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#load(java.io.Serializable)
	 */
	public T load(Serializable id) throws DataAccessException {		
		Object o = getHibernateTemplate().get(getEntityClass(), id);
		if (o == null)
			// 获取数据失败，找不到对应主键的数据
			throw new ObjectRetrievalFailureException(getEntityClass(), id);
		return (T) o;
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#save(T)
	 */
	public String save(T entity) throws DataAccessException {		
		String id= (String)getHibernateTemplate().save(entity);
		return id;
	}
	public void saveAll(Collection entityCollection) throws DataAccessException {
		getHibernateTemplate().saveOrUpdateAll(entityCollection);
		
	}
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#update(T)
	 */
	public void update(T entity) throws DataAccessException {
		getHibernateTemplate().update(entity);
		
	}
	
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#saveOrUpdate(T)
	 */
	public String saveOrUpdate(T entity){
		String id=null;
		try{
			if( (id=String.valueOf(BeanUtils.getDeclaredProperty(entity, this.getIdName())))==null)
				return (String)super.getHibernateTemplate().save(entity);
			else{
				super.getHibernateTemplate().saveOrUpdate(entity);
				return id;
			}
			
		}catch(Exception e){
			throw new WitException(e);
		}		
	}
	public void saveOrUpdateAll(Collection entityCollection){
//		this.getHibernateTemplate().saveOrUpdateAll(entityCollection);
		for(Object entity:entityCollection){
			saveOrUpdate((T)entity);
		}
		
	}
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#delete(java.io.Serializable)
	 */
	public void deleteById(final Serializable id) throws DataAccessException {
		getHibernateTemplate().delete(findById(id));		

	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#delete(T)
	 */
	public void delete(T entity) throws DataAccessException {
		getHibernateTemplate().delete(entity);
		

	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#deleteAll(java.util.Collection)
	 */
	public void deleteAll(Collection entities) throws DataAccessException {
		getHibernateTemplate().deleteAll(entities);
		
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#deleteBy(java.lang.String, java.lang.Object)
	 */
	public void deleteBy(String propertyName,Object value){
		List entities=this.findBy(propertyName, value);
		getHibernateTemplate().deleteAll(entities);
		
	}
	public void deleteAllByIds(String...ids) throws DataAccessException {
		for(String id:ids){
			getHibernateTemplate().delete(this.findById(id));			
		}
	}
	public void deleteAll(){
		getHibernateTemplate().deleteAll(getAll());
		
	}
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#findBy(org.hibernate.Criteria)
	 */
	public List<T> findBy(Criteria crit) {
		Criteria queryCriteria = crit;
		return queryCriteria.list();
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#findByHql(java.lang.String, java.lang.Object)
	 */
	public List findByHql(String hql, Object... values) {
		Assert.hasText(hql);
		return getHibernateTemplate().find(hql, values);
	}
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#findBy(java.util.Map)
	 */
	public List<T> findBy(Map<String,Object> filter){
		
		return findBy(createCriteria(filter));

	}
	public List<T> findBy(Map<String,Object> filter, String orderBy, boolean isAsc){
		Criteria criteria = createCriteria(filter);
		if (isAsc)
			criteria.addOrder(Order.asc(orderBy));
		else
			criteria.addOrder(Order.desc(orderBy));
		
		return findBy(criteria);

	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#findBy(java.lang.String, java.lang.Object)
	 */
	public List<T> findBy(String propertyName,Object value) {
		Assert.hasText(propertyName);
		if(value==null)
			return createCriteria(Restrictions.isNull(propertyName)).list();
		else
			return createCriteria(Restrictions.eq(propertyName, value)).list();
	}

	public T getBy(String propertyName,Object value) {
		Assert.hasText(propertyName);
		List list;
		if(value==null)
			list= createCriteria(Restrictions.isNull(propertyName)).list();
		else list= createCriteria(Restrictions.eq(propertyName, value)).list();
		if(list.size()>0)
			return (T) list.get(0);
		return null;
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#findBy(java.lang.String, java.lang.Object, java.lang.String, boolean)
	 */
	public List<T> findBy(String propertyName,Object value, String orderBy, boolean isAsc) {
		Assert.hasText(propertyName);
		Assert.hasText(orderBy);
		if(null==value)
			return createCriteria(orderBy, isAsc,Restrictions.isNull(propertyName)).list();
		return createCriteria(orderBy, isAsc,Restrictions.eq(propertyName, value)).list();
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#createQuery(java.lang.String, java.lang.Object)
	 */
	public Query createQuery(String hql, Object... values) {
		Assert.hasText(hql);
		Query query = getSession().createQuery(hql);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		return query;
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#pagedQuery(java.lang.String, com.westerasoft.wsframework.core.PageSortModel, java.lang.Object)
	 */

	public List pagedQuery(String hql, PageSortModel psm,Object... values) {
		Assert.hasText(hql);
		Assert.isTrue(psm.getPageNo() >= 1, "pageNo should start from 1");
		// Count查询
		String countQueryString = " select count (*) "+ removeSelect(removeOrders(hql));
		List countlist = getHibernateTemplate().find(countQueryString, values);
		long totalRows = (Long) countlist.get(0);
		psm.setTotalRows((int)totalRows);
		int startIndex = psm.getStartOfPage();
		Query query = createQuery(hql, values);
		return query.setFirstResult(startIndex).setMaxResults(psm.getPageSize())
				.list();

	}

	/**
	 * 分页查询
	 * @return List - 包含当前页数据的List
	 */
	private List<T> pagedQuery(final PageSortModel pas) {
		return getHibernateTemplate().executeFind(new HibernateCallback() {
			public Object doInHibernate(Session session)
					throws HibernateException{
				StringBuffer hq = new StringBuffer("from ");
				hq.append(getEntityClass().getName());
				hq.append(" op");
				String countHq="select count(*) " + hq.toString();
				Query query = session.createQuery(countHq);
				Object resultNumObj=query.uniqueResult();
				if (pas != null) {
					pas.setTotalRows(Integer.parseInt(resultNumObj.toString()));
				}
				query = session.createQuery(hq.toString());
				if (pas != null) {
					query.setFirstResult(pas.getStartOfPage());
					query.setMaxResults(pas.getPageSize());
				}
				List results = query.list();
				return results;
			}

		});
	}
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#pagedQuery(java.util.Map, com.westerasoft.wsframework.core.PageSortModel)
	 */
	public List<T> pagedQuery( Map filter, PageSortModel psm) {		
		
		List fieldNames = BeanUtils.getEntityFieldNames(getEntityClass(),BaseEntity.class,"");
		if(fieldNames.contains("id")&&psm.getOrderMap().size()<1)
			psm.addOrder("id","desc");
		Criteria criteria=createCriteria(filter);
		List criterionEntries=null;
		try {
			criterionEntries = (List)BeanUtils.getDeclaredProperty(criteria, "criterionEntries");
		} catch (Exception e) {
		}		
		if( //(criterionEntries==null||criterionEntries.size()<1)&&
				filter.containsKey("s_anonyme")&&StringUtils.isNotEmpty((String)filter.get("s_anonyme"))				
				){			
			return this.pagedQueryByFullText((String)filter.get("s_anonyme"), psm,criteria);
		}
		return this.pagedQuery(criteria, psm);
	}
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#pagedQuery(org.hibernate.Criteria, com.westerasoft.wsframework.core.PageSortModel)
	 */
	public List<T> pagedQuery(Criteria criteria,PageSortModel psm){
		if(!psm.getOrderMap().isEmpty()){
			return find(criteria,createOrders(psm.getOrderMap()),psm);
		}
		CriteriaImpl impl = (CriteriaImpl) criteria;
		Projection projection = impl.getProjection();
		List orderEntries;
		try {
			orderEntries = (List) BeanUtils.getDeclaredProperty(impl, "orderEntries");
			BeanUtils.setDeclaredProperty(impl, "orderEntries", new ArrayList());
		}
		catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}
		int totalCount =((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
		psm.setTotalRows(totalCount);
		if(psm.getPageNo()>psm.getPageCount()&&psm.getPageNo()>1){
			psm.resetPageNo(psm.getPageNo()-1);
		}
		criteria.setProjection(projection);
		if (projection == null) {
			criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		try {
			BeanUtils.setDeclaredProperty(impl, "orderEntries", orderEntries);
		} catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}

		if(!psm.isAll()){
			criteria.setFirstResult(psm.getStartOfPage());
			criteria.setMaxResults(psm.getPageSize());
		}
		List dataList=criteria.list();
		return dataList;

	}

	private List find(Criteria criteria,Order[] order,PageSortModel psm){
		CriteriaImpl impl = (CriteriaImpl) criteria;
		Projection projection = impl.getProjection();
		List orderEntries;
		try {
			orderEntries = (List) BeanUtils.getDeclaredProperty(impl, "orderEntries");
			BeanUtils.setDeclaredProperty(impl, "orderEntries", new ArrayList());
		}
		catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}
		int totalCount =((Integer) criteria.setProjection(Projections.rowCount()).uniqueResult()).intValue();
		psm.setTotalRows(totalCount);
		if(psm.getPageNo()>psm.getPageCount()&&psm.getPageNo()>1){
			psm.resetPageNo(psm.getPageCount()-1);
		}
		criteria.setProjection(null);
		if (projection == null) {
			criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		try {
			BeanUtils.setDeclaredProperty(impl, "orderEntries", orderEntries);
		} catch (Exception e) {
			throw new InternalError(" Runtime Exception impossibility throw ");
		}
		if(order!=null){
			for(int i=0;i<order.length;i++){
				if(order[i]!=null){
					String orderp=order[i].toString();
					if(!inField(orderp.substring(0,orderp.indexOf(" ")).trim())) continue;
					if(orderp.indexOf(".")>0){
						String a=orderp.substring(0,orderp.lastIndexOf("."));
						String alias=a.replaceAll("\\.","");
						criteria.createAlias(a,alias);
						String orderName=orderp.substring(orderp.lastIndexOf(".")+1,orderp.lastIndexOf(" "));
						if(StringUtils.countMatches(orderp, ".")==1){
							orderName=alias+"."+orderName;
							if(StringUtils.containsIgnoreCase(orderp, "asc")){
								order[i]=Order.asc(orderName);
							}
							else{
								order[i]=Order.desc(orderName);
							}
						}

					}
					criteria.addOrder(order[i]);
				}
			}
		}

		if(!psm.isAll()){
			criteria.setFirstResult(psm.getStartOfPage());
			criteria.setMaxResults(psm.getPageSize());
		}

		return criteria.list();
	}
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#createCriteria(org.hibernate.criterion.Criterion)
	 */
	public Criteria createCriteria(Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(getEntityClass());
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#createCriteria()
	 */
	public Criteria createCriteria() {
		return getSession().createCriteria(getEntityClass());

	}
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#createCriteria(java.util.Map)
	 */
	public Criteria createCriteria(Map filter) {
		Criteria criteria = createCriteria();
		List fieldNames = BeanUtils.getEntityFieldNames(getEntityClass(),BaseEntity.class,"");
		Map alias=new HashMap();
		if (filter != null) {
			Iterator iterator = filter.keySet().iterator();
			while (iterator.hasNext()) {
				String propertyName = (String) iterator.next();
				Object propertyValue = filter.get(propertyName);
				if(propertyValue!=null)
					propertyValue=convertValue(propertyName,String.valueOf(propertyValue));
				if (fieldNames.contains(propertyName)) {
					if (propertyValue != null&&!propertyValue.equals("")){
						if(propertyName.indexOf(".")>0){
							String path=propertyName.substring(0,propertyName.lastIndexOf("."));
							String pathAlias=path;
							if(path.indexOf(".")>0){
								pathAlias=path.substring(path.lastIndexOf(".")+1);
							}
							if(alias.get(path)==null){
								criteria.createAlias(path,pathAlias);
								alias.put(path, pathAlias);
							}
							criteria.add(Restrictions.eq(pathAlias+propertyName.substring(propertyName.lastIndexOf(".")),propertyValue));
						}else{
							criteria.add(Restrictions.eq(propertyName,propertyValue));
						}
					}else if(propertyValue==null){
						criteria.add(Restrictions.isNull(propertyName));
					}
				}
				if (propertyName.startsWith("s_expression")) {
					String pv = ((String) propertyValue).trim();
					String[] exp = StringUtils.split(pv.substring(pv.indexOf("(")+1, pv.length() - 1), ",");
					if (exp.length == 2 || exp.length == 3) {
						String expPropValue=(String)filter.get(exp[1].trim().substring(1));
						if(!exp[1].startsWith("_"))
								expPropValue = exp[1].trim();
						if(expPropValue!=null&&expPropValue.length()>0){
							propertyName=exp[0].trim();
							propertyValue=convertValue(propertyName, expPropValue);
							if(propertyValue instanceof java.util.Date){
								this.getDateGt((Date)propertyValue);
							}
							if(propertyValue!=null){
								if(propertyName.indexOf(".")>0){
									String sub=propertyName.substring(0,propertyName.indexOf("."));
									criteria.createAlias(sub,sub);
								}
							if (pv.startsWith("eq")) {
								if(propertyValue==null){
									criteria.add(Restrictions.isNull(propertyName));
								}else{
									criteria.add(Restrictions.eq(propertyName,propertyValue));
								}
							}else if(pv.startsWith("ne")){
								if(propertyValue==null){
									criteria.add(Restrictions.isNotNull(propertyName));
								}else{
									criteria.add(Restrictions.ne(propertyName,propertyValue));
								}
							}
							else if (pv.startsWith("ge")) {
								criteria.add(Restrictions.ge(propertyName,propertyValue));
							} else if (pv.startsWith("gt")) {
								criteria.add(Restrictions.gt(propertyName,propertyValue));
							} else if (pv.startsWith("le")) {
								criteria.add(Restrictions.le(propertyName,propertyValue));
							} else if (pv.startsWith("lt")) {
								criteria.add(Restrictions.lt(propertyName,propertyValue));
							} else if (pv.startsWith("in")) {
								try{
								criteria.add(Restrictions.in(propertyName,StringUtils.split((String)propertyValue,",")));
								}catch(Exception e){
									throw new WitException("参数值必须是字符串，这里是："+propertyValue.getClass()+":"+propertyValue);
								}
							} 
							else if (pv.startsWith("like")) {
								MatchMode matchMode = MatchMode.ANYWHERE;
								if (exp.length == 3) {
									if (exp[2].equals("L")) {
										matchMode = MatchMode.START;
									} else if (exp[2].equals("R")) {
										matchMode = MatchMode.END;
									}
								}
								criteria.add(Restrictions.like(propertyName,expPropValue, matchMode));
							} else if (pv.startsWith("between")) {
								if (exp.length == 3) {
									String expPropValue2 = (String) filter.get(exp[2].trim().substring(1));
									if (expPropValue2 == null)
										expPropValue2 = exp[2].trim();
									Object prop2Value=convertValue(propertyName, expPropValue2);
									if(propertyValue!=null)
										criteria.add(Restrictions.between(propertyName, expPropValue,prop2Value));
								}
							}
							}
						}
					}
				}
			}
		}

		return criteria;
	}
	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#createCriteria(java.lang.String, boolean, org.hibernate.criterion.Criterion)
	 */
	public Criteria createCriteria(String orderBy, boolean isAsc,
			Criterion... criterions) {
		Assert.hasText(orderBy);
		Criteria criteria = createCriteria(criterions);
		if (isAsc)
			criteria.addOrder(Order.asc(orderBy));
		else
			criteria.addOrder(Order.desc(orderBy));
		return criteria;
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#isUnique(T, java.lang.String)
	 */
	public boolean isUnique(T entity, String uniquePropertyNames) {
		Assert.hasText(uniquePropertyNames);
		Criteria criteria = createCriteria().setProjection(
				Projections.rowCount());
		String[] nameList = uniquePropertyNames.split(",");
		try {
			// 循环加入唯一列
			for (String name : nameList) {
				criteria.add(Restrictions.eq(name, PropertyUtils.getProperty(
						entity, name)));
			}
			// 以下代码为了如果是update的情况,排除entity自身.
			String idName = getIdName();
			// 取得entity的主键值
			Serializable id = getId(entity);
			// 如果id!=null,说明对象已存在,该操作为update,加入排除自身的判断
			if (id != null)
				criteria.add(Restrictions.not(Restrictions.eq(idName, id)));
		} catch (Exception e) {
			ReflectionUtils.handleReflectionException(e);
		}
		return (Integer)criteria.uniqueResult() == 0 ? true : false;
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#getId(T)
	 */
	public Serializable getId(T entity) throws NoSuchMethodException,
			IllegalAccessException, InvocationTargetException {
		Assert.notNull(entity);
		return (Serializable) PropertyUtils.getProperty(entity, getIdName());
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#getIdName()
	 */
	public String getIdName() {
		if(BaseEntity.class.isAssignableFrom(getEntityClass())){
			return "id";
		}else{
			ClassMetadata meta = getSessionFactory().getClassMetadata(getEntityClass());
			Assert.notNull(meta, "Class " + getEntityClass()+ " not define in hibernate session factory.");
			String idName = meta.getIdentifierPropertyName();	
			Assert.hasText(idName, getEntityClass().getSimpleName()	+ " has no identifier property define.");
			return idName;
		}
	}


	private Order[] createOrders(final Map maps) {
		List order = new ArrayList();
		Iterator keys = maps.keySet().iterator();
		while (keys.hasNext()) {
			String key = (String) keys.next();
			String value = (String) maps.get(key);
			if (null != value && !"".equals(value)) {
				if (value.equals("desc")) {
					order.add(Order.desc(key));
				} else if(value.equals("asc")){
					order.add(Order.asc(key));
				}
			}
		}
		return (Order[]) order.toArray(new Order[0]);
	}

	// 暂时不使用
	@SuppressWarnings("unused")
	private List pagedQuery(Criteria criteria, Order[] order,PageSortModel psm) {
		Assert.isNull(psm, "pageModel should is not null.");
		CriteriaImpl impl = (CriteriaImpl) criteria;
		Projection projection = impl.getProjection();
		List orderEntries;
		try {
			orderEntries = (List) BeanUtils.getDeclaredProperty(impl,
					"orderEntries");
			BeanUtils
					.setDeclaredProperty(impl, "orderEntries", new ArrayList());
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		int totalCount = ((Integer) criteria.setProjection(
				Projections.rowCount()).uniqueResult()).intValue();
		psm.setTotalRows(totalCount);
		if (psm.getPageNo()> psm.getPageCount() && psm.getPageNo() > 1) {
			psm.setPageNo(psm.getPageNo() - 1);
		}
		criteria.setProjection(null);
		if (projection == null) {
			criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
		}
		try {
			BeanUtils.setDeclaredProperty(impl, "orderEntries", orderEntries);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
		if (order != null) {
			for (int i = 0; i < order.length; i++) {
				if (order[i] != null) {
					String orderp=order[i].toString();
					if(orderp.indexOf(".")>0){
						String a=orderp.substring(0,orderp.indexOf("."));
						criteria.createAlias(a, a);
					}
					criteria.addOrder(order[i]);
				}
			}
		}
		if (!psm.isAll()) {
			criteria.setFirstResult(psm.getStartOfPage());
			criteria.setMaxResults(psm.getPageSize());
		}

		return criteria.list();
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#flush()
	 */
	public void flush() {
		getHibernateTemplate().flush();
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#clear()
	 */
	public void clear() {
		getHibernateTemplate().clear();
	}

	private static String removeSelect(String hql) {
		Assert.hasText(hql);
		int beginPos = hql.toLowerCase().indexOf("from");
		Assert.isTrue(beginPos != -1, " hql : " + hql
				+ " must has a keyword 'from'");
		return hql.substring(beginPos);
	}

	private static String removeOrders(String hql) {
		Assert.hasText(hql);
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*",Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}
	private Object convertValue(String fieldName, String propertyValue) {
		//Field[] fields = getEntityClass().getDeclaredFields();
		List<Field> fields=BeanUtils.getAllField(entityClass, BaseEntity.class);
		for (Field field : fields) {
			if (field.getName().equals(fieldName)) {
				Class fieldType = field.getType();
				return convertValue(fieldType,propertyValue);
			}else{
				if( field.getType().getSuperclass() ==BaseEntity.class){
					Field[] subFields = field.getType().getDeclaredFields();
					for (Field subField : subFields) {
						if(subField.isAnnotationPresent(NotQueryField.class)) continue;
						Class subFieldType = subField.getType();
						String subFieldName = subField.getName();
						Class subClass =BeanUtils.getClassByName(subFieldType.getName());
						if (subClass != null&& !subFieldName.equals(this.getIdName())) {
							String subEntityFieldName=field.getName() + "."+ subFieldName;
							if(subEntityFieldName.equals(fieldName))
								return convertValue(subFieldType,propertyValue);
						}
					}
				}
			}
		}
		return propertyValue;
	}
	private Object convertValue(Class fieldType, String propertyValue) {
		try {
			if (fieldType == String.class) {
				return propertyValue;
			} else if (fieldType == Double.class
					|| fieldType == double.class) {
				return Double.valueOf(propertyValue);//double
			} else if (fieldType == Integer.class
					|| fieldType == int.class) {
				return Integer.valueOf(propertyValue);//int
			} else if (fieldType == Float.class
					|| fieldType == float.class) {
				return Float.valueOf(propertyValue);//float
			} else if (fieldType == Long.class
					|| fieldType == long.class) {
				return Long.valueOf(propertyValue);//long
			} else if (fieldType == Short.class
					|| fieldType == short.class) {
				return Short.valueOf(propertyValue);//short
			} 
			else if (fieldType == java.util.Date.class) {
				return DateUtil.convertStringToDate(propertyValue);//date
			} else if (fieldType == java.sql.Timestamp.class) {
				return new TimestampConverter().convert(Date.class, propertyValue);//datatime
			} else if(fieldType == Byte.class
					|| fieldType == byte.class){
				return Byte.valueOf(propertyValue);//byte
			}
		} catch (Exception e) {
			return null;
		}
		return null;
	}	
	public Criteria createCriteriaForAll(String propertyValue,Criteria criteria ) {
		List<SimpleExpression> ls = new ArrayList();
		if(criteria==null)
			criteria=createCriteria();
		Disjunction c = Restrictions.disjunction();
		Field[] fields = getEntityClass().getDeclaredFields();
		for (Field field : fields) {
			Class fieldType = field.getType();
			String fieldName = field.getName();
			if(!field.isAnnotationPresent(NotQueryField.class)){
				if (!fieldName.equals(this.getIdName())) {
					Object v=convertValue(fieldType,propertyValue);
					if (fieldType == String.class) {
						ls.add(Restrictions.like(fieldName, "%" + propertyValue	+ "%"));
					}else if(v!=null){
						if(v instanceof java.util.Date){
							c.add(Restrictions.between(fieldName,v,getDateGt((Date)v)));
						}
						else ls.add(Restrictions.eq(fieldName,v));
					}
				}
			}
			//BaseEntity.class.isAssignableFrom(fieldType)
			if (fieldType.getSuperclass() == BaseEntity.class) {//can hava one sub entity
				Field[] subFields = fieldType.getDeclaredFields();
				for (Field subField : subFields) {
					if(subField.isAnnotationPresent(NotQueryField.class)) continue;
					Class subFieldType = subField.getType();
					String subFieldName = subField.getName();
					Class subClass =BeanUtils.getClassByName(fieldName);
					if (subClass != null&& !subFieldName.equals(this.getIdName())) {
						String subEntityFieldName=fieldName + "."+ subFieldName;
						Object v=convertValue(subFieldType,propertyValue);
							if (fieldType == String.class) {
								ls.add(Restrictions.like(subEntityFieldName, "%" + propertyValue	+ "%"));
							}else if(v!=null){
								if(v instanceof java.util.Date)
									c.add(Restrictions.between(subEntityFieldName,v,getDateGt((Date)v)));
								else ls.add(Restrictions.eq(subEntityFieldName,v));
							}
					}
				}
			}
		}

		for (SimpleExpression se : ls) {
			c.add(se);
		}
		return criteria.add(c);
	}

	/* (non-Javadoc)
	 * @see com.westerasoft.wsframework.dao.IDao#pagedQueryByFullText(java.lang.Object, com.westerasoft.wsframework.core.PageSortModel)
	 */
	public List pagedQueryByFullText(String propertyValue, PageSortModel psm,Criteria criteria) {
		return this.pagedQuery(createCriteriaForAll(propertyValue,criteria), psm);
	}
	private Date getDateGt(Date d){
		String strD=DateUtil.convertDateToString(d);
		strD +=" 23:59:59.99";
		try {
			return  DateUtil.convertStringToDate(DateUtil.getDateTimePattern(), strD);
		} catch (ParseException e) {
			e.printStackTrace();
			return d;
		}
	}
	public T merge(T entity){
		return (T)getHibernateTemplate().merge(entity);
	}


	public List<T> pagedQueryByNamedQuery(String queryName,PageSortModel psm)throws DataAccessException{
		Query query=super.getSession().getNamedQuery(queryName);
		String countQueryString = " select count (*) " + removeSelect(removeOrders(query.getQueryString()));
		Object resultNumObj=super.getSession().createSQLQuery(countQueryString).uniqueResult();
		psm.setTotalRows(Integer.parseInt(resultNumObj.toString()));
		query.setFirstResult(psm.getStartOfPage());
		query.setMaxResults(psm.getPageSize());
		return query.list();
	}

	public List<T> pagedQueryByNamedQuery(String queryName, Object value,PageSortModel psm) throws DataAccessException {
		return pagedQueryByNamedQuery(queryName, new Object[] {value},psm);
	}
	public List<T> pagedQueryByNamedQuery(final String queryName, final Object[] values,PageSortModel psm) throws DataAccessException {
		Query query=super.getSession().getNamedQuery(queryName);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		String countQueryString = " select count (*) " + removeSelect(removeOrders(query.getQueryString()));
		Object resultNumObj=super.getSession().createSQLQuery(countQueryString).uniqueResult();
		psm.setTotalRows(Integer.parseInt(resultNumObj.toString()));
		query.setFirstResult(psm.getStartOfPage());
		query.setMaxResults(psm.getPageSize());
		return query.list();
	}
	public List<T> pagedQueryByNamedQueryAndNamedParam(String queryName,String paramName, Object value,PageSortModel psm){
		return pagedQueryByNamedQueryAndNamedParam(queryName,new String[] {paramName}, new Object[] {value},psm);
	}
	public List<T> pagedQueryByNamedQueryAndNamedParam(String queryName,String[] paramNames, Object[] values,PageSortModel psm){
		if (paramNames.length != values.length) {
			throw new IllegalArgumentException("Length of paramNames array must match length of values array");
		}
		Query query=super.getSession().getNamedQuery(queryName);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				applyNamedParameterToQuery(query, paramNames[i], values[i]);
			}
		}
		String countQueryString = " select count (*) " + removeSelect(removeOrders(query.getQueryString()));
		Object resultNumObj=super.getSession().createSQLQuery(countQueryString).uniqueResult();
		psm.setTotalRows(Integer.parseInt(resultNumObj.toString()));
		query.setFirstResult(psm.getStartOfPage());
		query.setMaxResults(psm.getPageSize());
		return query.list();
	}


	private void applyNamedParameterToQuery(Query queryObject,
			String paramName, Object value) throws HibernateException {
		if (value instanceof Collection) {
			queryObject.setParameterList(paramName, (Collection) value);
		} else if (value instanceof Object[]) {
			queryObject.setParameterList(paramName, (Object[]) value);
		} else {
			queryObject.setParameter(paramName, value);
		}
	}

	public List findByNamedQuery(String queryName) throws DataAccessException {
		return super.getHibernateTemplate().findByNamedQuery(queryName);
	}


	public List findByNamedQuery(String queryName, Object value)
			throws DataAccessException {
		return super.getHibernateTemplate().findByNamedQuery(queryName, value);
	}

	public List findByNamedQuery(String queryName, Object[] values)
			throws DataAccessException {
		return super.getHibernateTemplate().findByNamedQuery(queryName, values);
	}

	public List findByNamedQueryAndNamedParam(String queryName,
			String paramName, Object value) {
		return super.getHibernateTemplate().findByNamedQueryAndNamedParam(queryName, paramName, value);
	}

	public List findByNamedQueryAndNamedParam(String queryName,
			String[] paramNames, Object[] values) throws DataAccessException {
		return super.getHibernateTemplate().findByNamedQueryAndNamedParam(queryName, paramNames, values);
	}
	private List<String> fields=null;
	private boolean inField(String name){
		if(fields==null)
			fields=BeanUtils.getEntityFieldNames(this.getEntityClass(),BaseEntity.class,"");
		for(String fname:fields){
			fname=fname.replace(this.getEntityClass().getSimpleName()+".","");
			if(name.equalsIgnoreCase(fname))
				return true;
		}
		return false;
	}
}
